<template>
    <div class="uploader-file" :status="status">
        <slot
            :file="file"
            :list="list"
            :status="status"
            :paused="paused"
            :error="error"
            :response="response"
            :average-speed="averageSpeed"
            :formated-average-speed="formatedAverageSpeed"
            :current-speed="currentSpeed"
            :is-complete="isComplete"
            :is-uploading="isUploading"
            :size="size"
            :formated-size="formatedSize"
            :uploaded-size="uploadedSize"
            :progress="progress"
            :progress-style="progressStyle"
            :progressing-class="progressingClass"
            :time-remaining="timeRemaining"
            :formated-time-remaining="formatedTimeRemaining"
            :type="type"
            :extension="extension"
            :file-category="fileCategory"
        >
            <div class="uploader-file-progress" :class="progressingClass" :style="progressStyle"></div>
            <div class="uploader-file-info">
                <div class="uploader-file-name"><i class="uploader-file-icon" :icon="fileCategory"></i>{{file.name}}</div>
                <div class="uploader-file-size">{{formatedSize}}</div>
                <!-- <div class="uploader-file-meta"></div> -->
                <div class="uploader-file-status">
                    <span v-show="status !== 'uploading'">{{statusText}}</span>
                    <span v-show="status === 'uploading'">
                        <span style="margin-right:5px;">{{progressStyle.progress}}</span>
                        <em style="margin-right:5px;">{{formatedAverageSpeed}}</em>
                        <i>{{formatedTimeRemaining}}</i>
                    </span>
                </div>
                <div class="uploader-file-actions">
                    <span class="uploader-file-pause" @click="pause"></span>
                    <span class="uploader-file-resume" @click="resume">️</span>
                    <span class="uploader-file-retry" @click="retry"></span>
                    <span class="uploader-file-remove" @click="remove"></span>
                </div>
            </div>
        </slot>
    </div>
</template>

<script>
    import Uploader from 'simple-uploader.js';
    import events from '../common/file-events';
    import { secondsToStr } from '../common/utils';

    const COMPONENT_NAME = 'uploader-file';

    export default {
        name:  COMPONENT_NAME,
        props: {
            file: {
                type: Object,
                default () {
                    return {};
                },
            },
            list: {
                type:    Boolean,
                default: false,
            },
        },
        data () {
            return {
                response:         null,
                paused:           false,
                error:            false,
                averageSpeed:     0,
                currentSpeed:     0,
                isComplete:       false,
                isUploading:      false,
                size:             0,
                formatedSize:     '',
                uploadedSize:     0,
                progress:         0,
                timeRemaining:    0,
                type:             '',
                extension:        '',
                progressingClass: '',
            };
        },
        computed: {
            fileCategory () {
                const extension = this.extension;
                const isFolder = this.file.isFolder;

                let type = isFolder ? 'folder' : 'unknown';
                const categoryMap = this.file.uploader.opts.categoryMap;
                const typeMap = categoryMap || {
                    image:    ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
                    video:    ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
                    audio:    ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
                    document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx'],
                };

                Object.keys(typeMap).forEach((_type) => {
                    const extensions = typeMap[_type];

                    if (extensions.indexOf(extension) > -1) {
                        type = _type;
                    }
                });
                return type;
            },
            progressStyle () {
                const progress = Math.floor(this.progress * 100);
                const style = `translateX(${Math.floor(progress - 100)}%)`;

                return {
                    progress:        `${progress}%`,
                    webkitTransform: style,
                    mozTransform:    style,
                    msTransform:     style,
                    transform:       style,
                };
            },
            formatedAverageSpeed () {
                return ` ${Uploader.utils.formatSize(this.averageSpeed)}/s `;
            },
            status () {
                const isUploading = this.isUploading;
                const isComplete = this.isComplete;
                const isError = this.error;
                const paused = this.paused;

                if (isComplete) {
                    return 'success';
                } else if (isError) {
                    return 'error';
                } else if (isUploading) {
                    return 'uploading';
                } else if (paused) {
                    return 'paused';
                } else {
                    return 'waiting';
                }
            },
            statusText () {
                const status = this.status;
                const fileStatusText = this.file.uploader.fileStatusText;

                let txt = status;

                if (typeof fileStatusText === 'function') {
                    txt = fileStatusText(status, this.response);
                } else {
                    txt = fileStatusText[status];
                }
                return txt || status;
            },
            formatedTimeRemaining () {
                const timeRemaining = this.timeRemaining;
                const file = this.file;

                if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
                    return '';
                }
                let parsedTimeRemaining = secondsToStr(timeRemaining);
                const parseTimeRemaining = file.uploader.opts.parseTimeRemaining;

                if (parseTimeRemaining) {
                    parsedTimeRemaining = parseTimeRemaining(timeRemaining, parsedTimeRemaining);
                }
                return parsedTimeRemaining;
            },
        },
        watch: {
            status (newStatus, oldStatus) {
                if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
                    this.tid = setTimeout(() => {
                        this.progressingClass = 'uploader-file-progressing';
                    }, 200);
                } else {
                    clearTimeout(this.tid);
                    this.progressingClass = '';
                }
            },
        },
        methods: {
            _actionCheck () {
                this.paused = this.file.paused;
                this.error = this.file.error;
                this.isUploading = this.file.isUploading();
            },
            pause () {
                this.file.pause();
                this._actionCheck();
                this._fileProgress();
            },
            resume () {
                this.file.resume();
                this._actionCheck();
            },
            remove () {
                this.file.cancel();
                this.$emit('file-removed');
            },
            retry () {
                this.file.retry();
                this._actionCheck();
            },
            processResponse (message) {
                let res = message;

                try {
                    res = JSON.parse(message);
                    // eslint-disable-next-line no-empty
                } catch (e) {}
                this.response = res;
            },
            fileEventsHandler (event, args) {
                const rootFile = args[0];
                const file = args[1];
                const target = this.list ? rootFile : file;

                // this.file become a proxy object in vue3
                if (this.file.uniqueIdentifier === target.uniqueIdentifier) {
                    if (this.list && event === 'fileSuccess') {
                        this.processResponse(args[2]);
                        return;
                    }
                    // eslint-disable-next-line prefer-spread
                    this[`_${event}`].apply(this, args);
                }
            },
            _fileProgress () {
                this.progress = this.file.progress();
                this.averageSpeed = this.file.averageSpeed;
                this.currentSpeed = this.file.currentSpeed;
                this.timeRemaining = this.file.timeRemaining();
                this.uploadedSize = this.file.sizeUploaded();
                this._actionCheck();
            },
            _fileSuccess (rootFile, file, message) {
                if (rootFile) {
                    this.processResponse(message);
                }
                this._fileProgress();
                this.error = false;
                this.isComplete = true;
                this.isUploading = false;
            },
            _fileComplete () {
                this._fileSuccess();
            },
            _fileError (rootFile, file, message) {
                this._fileProgress();
                this.processResponse(message);
                this.error = true;
                this.isComplete = false;
                this.isUploading = false;
            },
        },
        mounted () {
            const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed'];
            const fnProps = [
                'isComplete',
                'isUploading',
                {
                    key: 'size',
                    fn:  'getSize',
                },
                {
                    key: 'formatedSize',
                    fn:  'getFormatSize',
                },
                {
                    key: 'uploadedSize',
                    fn:  'sizeUploaded',
                },
                'progress',
                'timeRemaining',
                {
                    key: 'type',
                    fn:  'getType',
                },
                {
                    key: 'extension',
                    fn:  'getExtension',
                },
            ];

            staticProps.forEach(prop => {
                this[prop] = this.file[prop];
            });
            fnProps.forEach((fnProp) => {
                if (typeof fnProp === 'string') {
                    this[fnProp] = this.file[fnProp]();
                } else {
                    this[fnProp.key] = this.file[fnProp.fn]();
                }
            });

            const handlers = this._handlers = {};
            const eventHandler = (event) => {
                handlers[event] = (...args) => {
                    this.fileEventsHandler(event, args);
                };
                return handlers[event];
            };

            events.forEach((event) => {
                this.file.uploader.on(event, eventHandler(event));
            });
        },
        unmounted () {
            events.forEach((event) => {
                this.file.uploader.off(event, this._handlers[event]);
            });
            this._handlers = null;
        },
    };
</script>

<style>
  .uploader-file {
    position: relative;
    height: 50px;
    line-height: 50px;
    border-bottom: 1px solid #cdcdcd;
    overflow: hidden;
  }
  .uploader-file[status="waiting"] .uploader-file-pause,
  .uploader-file[status="uploading"] .uploader-file-pause {
    display: block;
  }
  .uploader-file[status="paused"] .uploader-file-resume {
    display: block;
  }
  .uploader-file[status="error"] .uploader-file-retry {
    display: block;
  }
  /* .uploader-file[status="success"] .uploader-file-remove {
    display: none;
  } */
  .uploader-file[status="error"] .uploader-file-progress {
    background: #ffe0e0;
  }
  .uploader-file-progress {
    position: absolute;
    width: 100%;
    height: 100%;
    background: #e2eeff;
    transform: translateX(-100%);
  }
  .uploader-file-progressing {
    transition: all .4s linear;
  }
  .uploader-file-info {
    position: relative;
    z-index: 1;
    height: 100%;
    white-space: nowrap;
    overflow: hidden;
  }
  .uploader-file-info:hover {
    background-color: rgba(240, 240, 240, 0.2);
  }
  .uploader-file-info i,
  .uploader-file-info em {
    font-style: normal;
  }
  .uploader-file-name,
  .uploader-file-size,
  .uploader-file-meta,
  .uploader-file-status,
  .uploader-file-actions {
    display: inline-block;
    vertical-align:top;
    position: relative;
    height: 100%;
  }
  .uploader-file-name {
    min-width: 60px;
    max-width: 200px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-indent: 14px;
  }
  .uploader-file-icon {
    width: 24px;
    height: 24px;
    display: inline-block;
    vertical-align: top;
    margin-top: 13px;
    margin-right: 8px;
  }
  .uploader-file-icon::before {
    content: "📃";
    display: block;
    height: 100%;
    font-size: 24px;
    line-height: 1;
    text-indent: 0;
  }
  .uploader-file-icon[icon="folder"]::before {
    content: "📂";
  }
  .uploader-file-icon[icon="image"]::before {
    content: "📊";
  }
  .uploader-file-icon[icon="video"]::before {
    content: "📹";
  }
  .uploader-file-icon[icon="audio"]::before {
    content: "🎵";
  }
  .uploader-file-icon[icon="document"]::before {
    content: "📋";
  }
  .uploader-file-size {
    min-width: 30px;
    padding:0 10px;
  }
  .uploader-file-status {
    font-size: 14px;
    min-width: 60px;
    padding-right: 10px;
  }
  .uploader-file-actions {
    min-width: 60px;
  }
  .uploader-file-actions > span {
    display: none;
    float: left;
    width: 16px;
    height: 16px;
    margin-top: 16px;
    margin-right: 10px;
    cursor: pointer;
    background: url("") no-repeat 0 0;
  }
  .uploader-file-actions > span:hover {
    background-position-x: -21px;
  }
  .uploader-file-actions .uploader-file-pause {
    background-position-y: 0;
  }
  .uploader-file-actions .uploader-file-resume {
    background-position-y: -17px;
  }
  .uploader-file-actions .uploader-file-retry {
    background-position-y: -53px;
  }
  .uploader-file-actions .uploader-file-remove {
    display: block;
    background-position-y: -34px;
    margin-left: auto;
    float: none;
  }
</style>
